// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-08-13

package src

import (
    "errors"
    "fmt"
    "gitee.com/go-libs/validator-iris/validator/validations"
    l0 "github.com/go-playground/locales"
    le "github.com/go-playground/locales/en"
    lz "github.com/go-playground/locales/zh"
    ut "github.com/go-playground/universal-translator"
    vv "github.com/go-playground/validator/v10"
    te "github.com/go-playground/validator/v10/translations/en"
    tz "github.com/go-playground/validator/v10/translations/zh"
    "reflect"
)

// Operator
// is a singleton instance for validate operations.
var Operator OperatorManager = (*operator)(nil)

type (
    // OperatorManager
    // is an interface for validate operations.
    OperatorManager interface {
        // Language
        // config operator language.
        Language(fn OperatorLanguageRegister, t l0.Translator) OperatorManager

        // LanguageWithChinese
        // config operator language as chinese.
        LanguageWithChinese() OperatorManager

        // LanguageWithEnglish
        // config operator language as english.
        LanguageWithEnglish() OperatorManager

        // Register
        // adds a validation to validate lib.
        Register(name string, validation func(fl vv.FieldLevel) bool) OperatorManager

        // Struct
        // validate a struct definition.
        Struct(v any) (err error)

        StructWithReader(v any, calls ...func(v any) error) (err error)
    }

    // OperatorLanguageRegister
    // is a type use to register validation language.
    OperatorLanguageRegister func(v *vv.Validate, ts ut.Translator) (err error)

    operator struct {
        enRegister, zhRegister       OperatorLanguageRegister
        enTranslation, zhTranslation l0.Translator
        translation                  ut.Translator
        validate                     *vv.Validate
    }
)

// +---------------------------------------------------------------------------+
// | Interface methods                                                         |
// +---------------------------------------------------------------------------+

func (o *operator) Language(fn OperatorLanguageRegister, t l0.Translator) OperatorManager {
    if o.translation = ut.New(t, t).GetFallback(); o.translation != nil {
        if err := fn(o.validate, o.translation); err != nil {
        }
    }
    return o
}

func (o *operator) LanguageWithChinese() OperatorManager {
    return o.Language(o.zhRegister, o.zhTranslation)
}

func (o *operator) LanguageWithEnglish() OperatorManager {
    return o.Language(o.enRegister, o.enTranslation)
}

func (o *operator) Register(name string, validation func(fl vv.FieldLevel) bool) OperatorManager {
    if err := o.validate.RegisterValidation(name, validation); err != nil {
    }
    return o
}

// Struct
// validate an any struct.
func (o *operator) Struct(v any) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("%v", r)
        }
    }()

    // Before validate.
    if x, ok := v.(ValidationBefore); ok {
        if err = x.BeforeValidate(); err != nil {
            return
        }
    }

    // Validate struct.
    if err = o.validate.Struct(v); err != nil {
        err = o.renderError(err)
        return
    }

    // After validate.
    if err == nil {
        if x, ok := v.(ValidationAfter); ok {
            err = x.AfterValidate()
        }
    }

    // Validate end.
    return
}

func (o *operator) StructWithReader(v any, calls ...func(v any) error) (err error) {
    for _, call := range calls {
        if err = call(v); err != nil {
            return
        }
    }

    return o.Struct(v)
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *operator) init() *operator {
    // Create
    // validate lib instance.
    o.validate = vv.New()

    // English
    // language for validate lib.
    o.enTranslation = le.New()
    o.enRegister = te.RegisterDefaultTranslations

    // Chinese
    // language for validate lib.
    o.zhTranslation = lz.New()
    o.zhRegister = tz.RegisterDefaultTranslations

    // Config
    // default language as chinese.
    o.LanguageWithChinese()

    // Return
    // initialized operator.
    return o.initTag().initValidations()
}

func (o *operator) initTag() *operator {
    o.validate.RegisterTagNameFunc(func(field reflect.StructField) (label string) {
        if label = field.Tag.Get("label"); label == "" {
            label = field.Name
        }
        return
    })
    return o
}

func (o *operator) initValidations() *operator {
    for name, call := range validations.ValidationMapper {
        o.Register(name, call)
    }
    return o
}

// renderError
// render validated error.
func (o *operator) renderError(in error) (err error) {
    // Return
    // if input param is nil.
    if in == nil {
        return
    }

    // Compared
    // error list container.
    var es = make(vv.ValidationErrors, 0)

    // Returns
    // a translated error if matched.
    if ok := errors.As(in, &es); ok {
        for _, e := range es {
            if str := e.Translate(o.translation); str != "" {
                err = fmt.Errorf(str)
                return
            }
        }
    }
    return
}
